﻿using System;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows;
using System.Globalization;
using System.Runtime.Serialization;

namespace Gencode.VECore.VectorShape
{
    public delegate void ChangedEventHandler(object sender, EventArgs e);

	public abstract class VECShape : Shape
	{
        public event ChangedEventHandler ShapeSizeChanged;
        public event ChangedEventHandler ShapeMoved;

        /// <summary>
        /// Needed for undo and redo functions
        /// </summary>
        int id; 

		public VECShape()
		{
            id = this.GetHashCode();
		}

        /// <summary>
        /// Clone this instance.
        /// </summary>
        public abstract VECShape Clone();

        bool selected = false;
        public bool Selected
        {
            get
            {
                return selected;
            }
            set
            {
                if (selecteable)
                    selected = value;
                else
                    selected = false;

                this.InvalidateVisual();
            }
        }

        bool selecteable = true;
        public bool Selecteable
        {
            get
            {
                return selecteable;
            }
            set
            {
                selecteable = value;
                if (!selecteable)
                {
                    selected = value;
                    this.InvalidateVisual();
                }
            }
        }

        /// <summary>
        /// HashID of the object
        /// </summary>
        public int ID
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }

        protected void CallMovedHandler()
        {
            if (ShapeMoved != null)
                ShapeMoved(this, new EventArgs());
        }

        protected void CallChangedHandler()
        {
            if (ShapeSizeChanged != null)
                ShapeSizeChanged(this, new EventArgs());
        }

		/// <summary>
		/// Get handle point by 1-based number
		/// </summary>
		/// <param name="handleNumber"></param>
		/// <returns></returns>
		public abstract Point GetHandle(int handleNumber);

		/// <summary>
		/// Hit test.
		/// Return value: -1 - no hit
		///                0 - hit anywhere
		///                > 1 - handle number
		/// </summary>
		/// <param name="point"></param>
		/// <returns></returns>
        public abstract int HitTest(Point point);

		/// <summary>
		/// Test whether point is inside of the object
		/// </summary>
		/// <param name="point"></param>
		/// <returns></returns>
        protected abstract bool PointInObject(Point point);

		/// <summary>
		/// Number of handles
		/// </summary>
        public abstract int HandleCount { get; }

        /// <summary>
        /// Move handle to the point
        /// </summary>
        /// <param name="point"></param>
        /// <param name="handleNumber"></param>
        public abstract void MoveHandle(Point point, int handleNumber);

        public abstract void Move(double deltaX, double deltaY);

        /// <summary>
        /// Test whether object intersects with rectangle
        /// </summary>
        /// <param name="rectangle"></param>
        /// <returns></returns>
        public abstract bool IntersectsWith(Rect rectangle);

		/// <summary>
		/// Get handle rectangle by 1-based number
		/// </summary>
		/// <param name="handleNumber"></param>
		/// <returns></returns>
		public virtual Rect GetHandleRectangle(int handleNumber)
		{
			Point point = GetHandle(handleNumber);

			return new Rect(point.X - 3, point.Y - 3, 7, 7);
		}

		/// <summary>
		/// Get cursor for the handle
		/// </summary>
		/// <param name="handleNumber"></param>
		/// <returns></returns>
		public virtual System.Windows.Input.Cursor GetHandleCursor(int handleNumber)
		{
			return System.Windows.Input.Cursors.Arrow;
		}

        /// <summary>
        /// Copy fields from this instance to cloned instance drawObject.
        /// Called from Clone functions of derived classes.
        /// </summary>
        protected void CopyShapeProperies(VECShape shape)
        {
            shape.Selected = this.Selected;
            shape.Stroke = this.Stroke;
            shape.StrokeThickness = this.StrokeThickness;
            shape.Fill = this.Fill;
            shape.ID = this.ID;
        }

        #region Serialization

        #region VecSerialization

        // Entry names for serialization
        private const string entryFill = "Fill";
        private const string entryStroke = "Stroke";
        private const string entryStrokeThikness = "StrokeThikness";

        /// <summary>
        /// Save object to serialization stream
        /// </summary>
        /// <param name="info"></param>
        /// <param name="orderNumber"></param>
        public virtual void SaveToStream(SerializationInfo info, int orderNumber)
        {
            info.AddValue(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "A", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Stroke).Color.A);
            info.AddValue(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "R", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Stroke).Color.R);
            info.AddValue(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "G", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Stroke).Color.G);
            info.AddValue(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "B", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Stroke).Color.B);

            info.AddValue(
                String.Format(CultureInfo.InvariantCulture,
                "{0}{1}",
                entryStrokeThikness, orderNumber),
                StrokeThickness);

            if (Fill != null)
            {
                info.AddValue(
                    String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryFill, "A", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Fill).Color.A);

                info.AddValue(
                    String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryFill, "R", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Fill).Color.R);

                info.AddValue(
                    String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryFill, "G", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Fill).Color.G);

                info.AddValue(
                    String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryFill, "B", orderNumber),
                    ((System.Windows.Media.SolidColorBrush)Fill).Color.B);
            }
        }

        /// <summary>
        /// Load object from serialization stream
        /// </summary>
        /// <param name="info"></param>
        /// <param name="orderNumber"></param>
        public virtual void LoadFromStream(SerializationInfo info, int orderNumber)
        {
            byte a = info.GetByte(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "A", orderNumber));
            byte r = info.GetByte(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "R", orderNumber));
            byte g = info.GetByte(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "G", orderNumber));
            byte b = info.GetByte(
                String.Format(CultureInfo.InvariantCulture,
                    "{0}{1}{2}",
                    entryStroke, "B", orderNumber));

            Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(a, r, g, b));

            StrokeThickness = info.GetInt32(
                String.Format(CultureInfo.InvariantCulture,
                "{0}{1}",
                entryStrokeThikness, orderNumber));

            try
            {
                a = info.GetByte(
                    String.Format(CultureInfo.InvariantCulture,
                        "{0}{1}{2}",
                        entryFill, "A", orderNumber));
                r = info.GetByte(
                    String.Format(CultureInfo.InvariantCulture,
                        "{0}{1}{2}",
                        entryFill, "R", orderNumber));
                g = info.GetByte(
                    String.Format(CultureInfo.InvariantCulture,
                        "{0}{1}{2}",
                        entryFill, "G", orderNumber));
                b = info.GetByte(
                    String.Format(CultureInfo.InvariantCulture,
                        "{0}{1}{2}",
                        entryFill, "B", orderNumber));

                Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(a, r, g, b));
            }
            catch (SerializationException)
            {
                //no fill color
                Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Transparent);
            }

            id = this.GetHashCode();
        }

        #endregion

        #endregion

        #region Static helper classes

        public static VECPolygon ToPolygon(VECRectangle rectangle)
        {
            VECPolygon polygon = new VECPolygon(new Point(rectangle.X, rectangle.Y));

            //polygon.AddSegment(new Point(rectangle.X + rectangle.Width, rectangle.Y));
            polygon.MoveHandle(new Point(rectangle.X + rectangle.Width, rectangle.Y), polygon.MaxHandle);
            polygon.AddSegment(new Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height));
            polygon.AddSegment(new Point(rectangle.X, rectangle.Y + rectangle.Height));
            //polygon.AddSegment(new Point(rectangle.X, rectangle.Y));

            polygon.JoinEndSegments();

            polygon.StrokeThickness = rectangle.StrokeThickness;
            polygon.Stroke = rectangle.Stroke;
            polygon.Fill = rectangle.Fill;

            return polygon;
        }

        #endregion


        //public virtual void Draw(System.Windows.Media.DrawingContext drawingContext)
        //{
        //    if (Selected)
        //    {
        //        DrawTracker(drawingContext);
        //    }
        //}

        ///// <summary>
        ///// Draw tracker for selected object.
        ///// </summary>
        //public virtual void DrawTracker(System.Windows.Media.DrawingContext drawingContext)
        //{
        //}
    }
}
